package com.fancy.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import com.fancy.common.results.Results;
import com.fancy.common.utils.http.HttpUtil;
import com.fancy.common.utils.http.HttpUtil.HTTPMethod;
import com.google.common.base.Function;
import com.fancy.common.utils.FileUtil;
import com.fancy.common.StoreFileTypeEnum;
import com.fancy.common.utils.StringUtil;
/**
 * 文件存储工具类
 * 
 * @author wlei 2013-3-16下午3:53:27
 * @version 1.0.0
 * @category 杭州广桥网络技术有限公司(商业先知)
 */
public class FileUtil {

	/**
	 * 日志记录器
	 */
	private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);

	// 临时文件目录
	private final static File TEMP_FILE_DIR = new File(Config.getString("temp.file.dir"));

	// 存储根目录
	private final static String UPLOAD_ROOT = Config.getString("upload.image.root");
	// 允许的文件类型
	
	private final static String ALLOW_TYPE = Config.getString("upload.image.type");
	
	// 允许的文件大小
	private final static Long MAX_SIZE = Config.getLong("upload.image.size");

	public static final long KB = 1024;
	public static final long MB = KB*1024;
	public static final long GB = MB*1024;
	// 年月日来分的文件目录
	private static final DateFormat PATH_FORMAT = new SimpleDateFormat("yyyy/MM/dd/");

	// 又拍空间名
	private static final String BUCKET_NAME = Config.getString("upyun.bucketName");
	// 又拍操作员名
	private static final String USER_NAME = Config.getString("upyun.userName");
	// 又拍操作员密码
	private static final String USER_PWD = Config.getString("upyun.userPwd");

	/**
	 * 拷贝图片文件到又拍,并且生成各种设备的大小,原图会被丢弃
	 * 
	 * @param filePath
	 *            不带顶层目录文件的保存路径
	 * @return
	 * @throws IOException
	 */
	public static boolean copyImageToUpYun(final String filePath) throws IOException 
	{

		// 初始化空间
		UpYun upyun = new UpYun(BUCKET_NAME, USER_NAME, USER_PWD);

		// 本地待上传的图片文件
		File file = new File(UPLOAD_ROOT, filePath);

		// 设置待上传文件的 Content-MD5 值
		// 如果又拍云服务端收到的文件MD5值与用户设置的不一致，将回报 406 NotAcceptable 错误
		upyun.setContentMD5(UpYun.md5(file));

		// 设置待上传文件的"访问密钥"
		// 注意：
		// 仅支持图片空！，设置密钥后，无法根据原文件URL直接访问，需带URL后面加上（缩略图间隔标志符+密钥）进行访问
		// 举例：
		// 如果缩略图间隔标志符为"!"，密钥为"bac"，上传文件路径为"/folder/test.jpg"，
		// 那么该图片的对外访问地址为：http://空间域名 /folder/test.jpg!bac
		// upyun.setFileSecret("bac");

		// 上传文件，并自动创建父级目录（最多10级）
		boolean result = upyun.writeFile(filePath, file, true);

		// 上传失败
		if (!result) 
		{
			logger.error("copy to upyun error. file:[]", file.getAbsolutePath());
		}

		// 获取上传文件后的信息（仅图片空间有返回数据）
		if (logger.isDebugEnabled()) 
		{
			// 本地文件
			logger.debug("local img file:{}", file.getAbsoluteFile());
			// 访问URL
			logger.debug("upyun url:{}", URIs.renderURI(URIs.getImgServer(), filePath));
			// 图片宽度
			logger.debug("upyun img width:{}", upyun.getPicWidth());
			// 图片高度
			logger.debug("upyun img height:{}", upyun.getPicHeight());
			// 图片帧数
			logger.debug("upyun img frame:{}", upyun.getPicFrames());
			// 图片类型
			logger.debug("upyun img type:{}", upyun.getPicType());
		}

		return result;
	}
		/**
		 * 保存文件到本地
		 * 
		 * @param is
		 * @param fileType
		 * @return
		 */
		public static Results<String> saveFile(final InputStream is, final String extension, final String fileType,final String uniqueTag) {
			Assert.notNull(is, "is not be null");
			Assert.notNull(fileType, "fileType not be null");

			// 取得文件保存目录，不存在则生成之
			final String savePath = (StringUtil.isEmpty(uniqueTag)?"":uniqueTag+"/")+(fileType.equals(StoreFileTypeEnum.TEMPLATE.toString())?"":getSavePath(fileType));
			final File pathFile = new File(UPLOAD_ROOT, savePath);
			if (pathFile.exists() == false) {
				pathFile.mkdirs();
			}
			
			final String filePath = savePath + getName() + extension;
			final Results<String> result = new Results<String>();
			result.setObject(filePath);

			// 保存文件
			try (FileOutputStream fos = new FileOutputStream(new File(UPLOAD_ROOT, filePath))) {
				FileCopyUtils.copy(is, fos);
				result.setSuccess(true);
			} catch (IOException e) {
				logger.error("save file to:[],[]", filePath, e);
				result.setMessage("save file error");
				result.setSuccess(false);
			}
			return result;
		}
		
		public static Results<String> saveFileNoRandom(final InputStream is, final String filename, final String fileType,final String uniqueTag) {
			Assert.notNull(is, "is not be null");
			Assert.notNull(fileType, "fileType not be null");

			// 取得文件保存目录，不存在则生成之
			final String savePath = (StringUtil.isEmpty(uniqueTag)?"":uniqueTag+"/")+(fileType.equals(StoreFileTypeEnum.TEMPLATE.toString())?"":getSavePathByDate(fileType));
			final File pathFile = new File(UPLOAD_ROOT, savePath);
			if (pathFile.exists() == false) {
				pathFile.mkdirs();
			}
			
			final String filePath = savePath + filename;
			final Results<String> result = new Results<String>();
			result.setObject(filePath);
			
			// 保存文件
			try {
				File newfile = new File(UPLOAD_ROOT, filePath);
				if(newfile.exists() && newfile.isFile())
					newfile.delete();
				FileOutputStream fos = new FileOutputStream(newfile);
				FileCopyUtils.copy(is, fos);
				result.setSuccess(true);
			} catch (IOException e) {
				logger.error("save file to:[],[]", filePath, e);
				result.setMessage("save file error");
				result.setSuccess(false);
			}

			return result;
		}

		/**
		 * 保存指向文件到本地
		 * 
		 * @param url
		 * @param fileType
		 * @return
		 */
		public static Results<String> saveFile(final String url, final String fileType,final String uniqueTag) {
			Assert.notNull(url, "url not be null");
			Assert.notNull(fileType, "fileType not be null");

			// 扩展名
			final String suffix = StringUtil.getSuffixName(url);
			// 模拟浏览器请求的方式，读取网络文件
			return HttpUtil.requestStream(HTTPMethod.GET, url, new Function<InputStream, Results<String>>() {
				@Override
				public Results<String> apply(InputStream input) {
					return saveFile(input, suffix, fileType,uniqueTag);
				}
			}, true);
		}
		
		public static Results<String> saveFile(final String url, final String fileType) {
			Assert.notNull(url, "url not be null");
			Assert.notNull(fileType, "fileType not be null");

			// 扩展名
			final String suffix = StringUtil.getSuffixName(url);
			// 模拟浏览器请求的方式，读取网络文件
			return HttpUtil.requestStream(HTTPMethod.GET, url, new Function<InputStream, Results<String>>() {
				@Override
				public Results<String> apply(InputStream input) {
					return saveFile(input, suffix, fileType,null);
				}
			}, true);
		}

		/**
		 * 保存文件到本地
		 * 
		 * @param file
		 * @param fileType
		 * @return
		 */
		public static Results<String> saveFile(final File file, final String fileType,final String uniqueTag) {
			Assert.notNull(file, "file not be null");
			Assert.notNull(fileType, "fileType not be null");

			try {
				// 扩展名
				String suffix = StringUtil.getSuffixName(file.getName());
				return saveFile(new FileInputStream(file), suffix, fileType,uniqueTag);
			} catch (FileNotFoundException e) {
				final Results<String> result = new Results<String>();
				logger.error("save multipart file error", e);
				result.setMessage("save multipart file error");
				result.setSuccess(false);
				return result;
			}
		}

		/**
		 * 保存文件到本地
		 * 
		 * @param file
		 * @param fileType
		 * @return
		 */
		public static Results<String> saveFile(final MultipartFile file, final String fileType,final String uniqueTag) {
			Assert.notNull(file, "file not be null");
			Assert.notNull(fileType, "fileType not be null");
			final Results<String> result = new Results<String>();
			try {
				// 扩展名
				String suffix = StringUtil.getSuffixName(file.getOriginalFilename());
				Long size = file.getSize();
				if(fileType.equals(StoreFileTypeEnum.IMAGES.toString()))
				{
					if (checkType(StoreFileTypeEnum.IMAGES.toString(),suffix)) 
					{
						if (size <= MAX_SIZE)
							return saveFile(file.getInputStream(), suffix, fileType,null);
						else
							result.setMessage("文件不能超过"+getFriendSizeTip(size)+"!");
					}
					else
					{
						result.setMessage("只能上传"+ALLOW_TYPE+"类型的图片");
					}
				}
				else if(fileType.equals(StoreFileTypeEnum.FILES.toString())){
					return saveFile(file.getInputStream(), suffix, fileType,null);
				}
				else
				{
					result.setMessage("未识别文件类型!");	
				}
				result.setSuccess(false);		
				return result;
			} catch (IOException e) {
				
				logger.error("save multipart file error", e);
				result.setMessage("save multipart file error");
				result.setSuccess(false);
				return result;
			}
		}
		
		public static Results<String> saveFile(final MultipartFile file, final String fileType) {
			return saveFile(file,fileType,null);
		}

		/**
		 * 保存图片文件到本地，大小限制10M，且只允许（gif,png,bmp,jpg,jpeg）格式文件
		 * 
		 * @param image
		 * @param fileType
		 * @return
		 */
		public static Results<String> saveImage(final MultipartFile image, final String fileType,final String uniqueTag) {
			Assert.notNull(image, "image not be null");
			Assert.notNull(fileType, "fileType not be null");
			return saveFile(image, fileType,uniqueTag);
		}
		
		public static Results<String> saveImage(final MultipartFile image, final String fileType) {
			Assert.notNull(image, "image not be null");
			Assert.notNull(fileType, "fileType not be null");
			return saveFile(image, fileType,null);
		}

		/**
		 * 生成临时文件到指定目录
		 * 
		 * @param value
		 * @return
		 * @throws IOException
		 */
		public static File createTempFile(final String prefix, final String suffix) throws IOException {
			if (!TEMP_FILE_DIR.exists()) {
				TEMP_FILE_DIR.mkdirs();
			}
			return File.createTempFile(prefix + "_", "." + suffix, TEMP_FILE_DIR);
		}

		/**
		 * 判断文件类型是否合法
		 * 
		 * @param type
		 * @return
		 */
		private static Boolean checkType(final String checkType ,final String type) {
			if(checkType.equals(StoreFileTypeEnum.IMAGES.toString()))
			{
				if(StringUtil.isEmpty(ALLOW_TYPE))
					return true;
				if (ALLOW_TYPE.contains(type)) 
				{
					return true;
				} 
				else 
				{
					return false;
				}
			}
			return false;
		}
		
		private static String getFriendSizeTip(final long size){
			if(size<KB)
				return size+"B";
			if(size<MB)
				return size*1.0/KB+"KB";
			if(size<GB)
				return size*1.0/MB+"MB";
			return size*1.0/GB+"GB";
		}

		/**
		 * 获取文件路径
		 * 
		 * @param fileType
		 * @return
		 */
		private static String getSavePathByDate(final String fileType) {
			return fileType.toString() + "/" + PATH_FORMAT.format(new Date());
		}
		
		private static String getSavePath(final String fileType) {
			return fileType.toString() + "/";
		}

		/**
		 * 获取文件名称(采用GUID)
		 * 
		 * @return
		 */
		private static String getName() {
			return UUID.randomUUID().toString();
		}
		
		/** 
	     * 复制单个文件 
	     *  
	     * @param srcFileName 
	     *            待复制的文件名 
	     * @param descFileName 
	     *            目标文件名 
	     * @param overlay 
	     *            如果目标文件存在，是否覆盖 
	     * @return 如果复制成功返回true，否则返回false 
	     */  
	    public static boolean copyFile(String srcFileName, String destFileName,  
	            boolean overlay) {  
	    	String MESSAGE = "";
	        File srcFile = new File(srcFileName);  
	  
	        // 判断源文件是否存在  
	        if (!srcFile.exists()) {  
	            MESSAGE = "源文件：" + srcFileName + "不存在！";   
	            return false;  
	        } else if (!srcFile.isFile()) {  
	            MESSAGE = "复制文件失败，源文件：" + srcFileName + "不是一个文件！";  
	            return false;  
	        }  
	  
	        // 判断目标文件是否存在  
	        File destFile = new File(destFileName);  
	        if (destFile.exists()) {  
	            // 如果目标文件存在并允许覆盖  
	            if (overlay) {  
	                // 删除已经存在的目标文件，无论目标文件是目录还是单个文件  
	                new File(destFileName).delete();  
	            }  
	        } else {  
	            // 如果目标文件所在目录不存在，则创建目录  
	            if (!destFile.getParentFile().exists()) {  
	                // 目标文件所在目录不存在  
	                if (!destFile.getParentFile().mkdirs()) {  
	                    // 复制文件失败：创建目标文件所在目录失败  
	                    return false;  
	                }  
	            }  
	        }  
	  
	        // 复制文件  
	        int byteread = 0; // 读取的字节数  
	        InputStream in = null;  
	        OutputStream out = null;  
	  
	        try {  
	            in = new FileInputStream(srcFile);  
	            out = new FileOutputStream(destFile);  
	            byte[] buffer = new byte[1024];  
	  
	            while ((byteread = in.read(buffer)) != -1) {  
	                out.write(buffer, 0, byteread);  
	            }  
	            return true;  
	        } catch (FileNotFoundException e) {  
	            return false;  
	        } catch (IOException e) {  
	            return false;  
	        } finally {  
	            try {  
	                if (out != null)  
	                    out.close();  
	                if (in != null)  
	                    in.close();  
	            } catch (IOException e) {  
	                e.printStackTrace();  
	            }  
	        }  
	    }  
	  
	    /** 
	     * 复制整个目录的内容 
	     *  
	     * @param srcDirName 
	     *            待复制目录的目录名 
	     * @param destDirName 
	     *            目标目录名 
	     * @param overlay 
	     *            如果目标目录存在，是否覆盖 
	     * @return 如果复制成功返回true，否则返回false 
	     */  
	    public static boolean copyDirectory(String srcDirName, String destDirName,  
	            boolean overlay) {  
	    	String MESSAGE = "";
	        // 判断源目录是否存在  
	        File srcDir = new File(srcDirName);  
	        if (!srcDir.exists()) {  
	            MESSAGE = "复制目录失败：源目录" + srcDirName + "不存在！";  
	            return false;  
	        } else if (!srcDir.isDirectory()) {  
	            MESSAGE = "复制目录失败：" + srcDirName + "不是目录！";  
	            return false;  
	        }  
	  
	        // 如果目标目录名不是以文件分隔符结尾，则加上文件分隔符  
	        if (!destDirName.endsWith(File.separator)) {  
	            destDirName = destDirName + File.separator;  
	        }  
	        File destDir = new File(destDirName);  
	        // 如果目标文件夹存在  
	        if (destDir.exists()) {  
	            // 如果允许覆盖则删除已存在的目标目录  
	            if (overlay) {  
	                new File(destDirName).delete();  
	            } else {  
	                MESSAGE = "复制目录失败：目的目录" + destDirName + "已存在！";   
	                return false;  
	            }  
	        } else {  
	            // 创建目的目录  
	            if (!destDir.mkdirs()) {  
	            	MESSAGE = "复制目录失败：创建目的目录失败！";  
	                return false;  
	            }  
	        }  
	  
	        boolean flag = true;  
	        File[] files = srcDir.listFiles();  
	        for (int i = 0; i < files.length; i++) {  
	            // 复制文件  
	            if (files[i].isFile()) {  
	                flag = FileUtil.copyFile(files[i].getAbsolutePath(),  
	                        destDirName + files[i].getName(), overlay);  
	                if (!flag)  
	                    break;  
	            } else if (files[i].isDirectory()) {  
	                flag = FileUtil.copyDirectory(files[i].getAbsolutePath(),  
	                        destDirName + files[i].getName(), overlay);  
	                if (!flag)  
	                    break;  
	            }  
	        }  
	        if (!flag) {  
	            MESSAGE = "复制目录" + srcDirName + "至" + destDirName + "失败！";  
	            return false;  
	        } else {  
	            return true;  
	        }  
	    }  
	    /**
	     * 得到文件路径结尾有/的路径
	     * @param path
	     * @return
	     */
	    public static String getFilePath(String path){
	    	if(StringUtils.isBlank(path)){
	    		return "";
	    	}
			if (!path.endsWith("/")) {  
				path = path + "/";  
			} 
			return path;
		}
	    
	    public static void main(String[] args) {
			System.out.println(StringUtil.getSuffixName("新建文档.txt"));
			
		}
}
